package gsp

import (
	"fmt"
	"reflect"
	"strings"

	"gorm.io/gorm"
)

type BaseDao struct {
	DB        *gorm.DB
	modelPtr  reflect.Type
	modelType reflect.Type
	zero      reflect.Value
}

// Init 初始化BaseDao
func (dao *BaseDao) Init(db *gorm.DB, model interface{}) {
	t := reflect.TypeOf(model)
	if t.Kind() == reflect.Ptr {
		dao.zero = reflect.Zero(t)
		dao.modelType = t.Elem()
	} else {
		dao.modelType = t
	}
	dao.DB = db
}

func (dao *BaseDao) Insert(value interface{}) error {
	return dao.DB.Create(value).Error
}

func (dao *BaseDao) Update(value interface{}) error {
	return dao.DB.Save(value).Error
}

func (dao *BaseDao) Delete(value interface{}, conds ...interface{}) error {
	db := dao.DB.Model(value).Debug()
	if len(conds) > 1 {
		db = db.Where(conds[0], conds[1:]...)
	} else if len(conds) == 1 {
		db = db.Where(conds[0])
	}

	return db.Delete(value).Error
}

func (dao *BaseDao) DeleteById(value interface{}, id uint64, conds ...interface{}) {
	db := dao.DB.Model(value).Debug()
	if len(conds) > 1 {
		db = db.Where(conds[0], conds[1:]...)
	} else if len(conds) == 1 {
		db = db.Where(conds[0])
	}
	err := db.Delete(value, id).RowsAffected
	if err != 0 {
		return
	}
	reflect.ValueOf(value).Elem().Set(dao.zero)
}

func (dao *BaseDao) DeleteBatchIds(value interface{}, ids []uint64, conds ...interface{}) error {
	db := dao.DB.Model(value)
	if len(conds) > 1 {
		db = db.Where(conds[0], conds[1:]...)
	} else if len(conds) == 1 {
		db = db.Where(conds[0])
	}
	return db.Delete(value, ids).Error
}

func (dao *BaseDao) SelectOne(value interface{}, conds ...interface{}) {
	//fmt.Println("SelectOne first: ", value, reflect.ValueOf(value).Elem())
	err := dao.DB.First(value, conds...).Error
	if err == nil {
		return
	}
	//fmt.Println("SelectOne: ", value, reflect.ValueOf(value).Elem())
	reflect.ValueOf(value).Elem().Set(dao.zero)
	return
}

func (dao *BaseDao) SelectOneWithJoin(value interface{}, joinName string, conds ...interface{}) {
	//fmt.Println("SelectOne first: ", value, reflect.ValueOf(value).Elem())
	maxIndex := len(conds) / 2
	querys := make([]string, 0)
	cond := make([]interface{}, 0)
	for i := 0; i < maxIndex; i++ {
		if conds[2*i+1] == "" {
			continue
		}
		str1, _ := conds[2*i].(string)
		querys = append(querys, fmt.Sprintf("%s = ?", str1))
		cond = append(cond, conds[2*i+1])
	}
	result := strings.Join(querys, fmt.Sprintf(" %s ", joinName))
	cond = append([]interface{}{result}, cond...)
	err := dao.DB.First(value, cond[0:]...).Error
	if err == nil {
		return
	}
	//fmt.Println("SelectOne: ", value, reflect.ValueOf(value).Elem())
	reflect.ValueOf(value).Elem().Set(dao.zero)
	return
}

func (dao *BaseDao) SelectById(value interface{}, id uint64) {
	err := dao.DB.First(value, id).Error
	if err == nil {
		return
	}

	reflect.ValueOf(value).Elem().Set(dao.zero)
	return
}

// 根据 条件，查询总记录数
func (dao *BaseDao) SelectCount(value interface{}, conds ...interface{}) (total int64) {
	db := dao.DB.Model(value)
	if len(conds) > 1 {
		db = db.Where(conds[0], conds[1:]...)
	} else if len(conds) == 1 {
		db = db.Where(conds[0])
	}
	err := db.Count(&total).Error
	if err != nil {
		panic(err)
	}
	return total
}

func (dao *BaseDao) SelectPage(value interface{}, page int, pageSize int, conds ...interface{}) (total int64) {
	db := dao.DB.Model(value)
	if len(conds) > 1 {
		db = db.Where(conds[0], conds[1:]...)
	} else if len(conds) == 1 {
		db = db.Where(conds[0])
	}
	err := db.Count(&total).Error
	if err != nil {
		panic(err)
	}
	limit := pageSize
	offset := pageSize * (page - 1)
	err = db.Limit(limit).Offset(offset).Find(value).Error
	if err != nil {
		panic(err)
	}

	return total
}

func (dao *BaseDao) SelectList(value interface{}, conds ...interface{}) {
	db := dao.DB.Model(value)
	if len(conds) > 1 {
		db = db.Where(conds[0], conds[1:]...)
	} else if len(conds) == 1 {
		db = db.Where(conds[0])
	}
	err := db.Find(value).Error
	if err != nil {
		panic(err)
	}
}

// SelectBatchIds 查询（根据ID 批量查询）
func (dao *BaseDao) SelectBatchIds(value interface{}, ids []uint64, conds ...interface{}) {
	db := dao.DB.Model(value)
	if len(ids) > 0 {
		db = db.Where(ids)
	}
	if len(conds) > 1 {
		db = db.Where(conds[0], conds[1:]...)
	} else if len(conds) == 1 {
		db = db.Where(conds[0])
	}
	err := db.Find(value).Error
	if err != nil {
		panic(err)
	}
}
